Skip to content

Prefer Flash Attn 2 instead of Fused Attn for THD dropout on Blackwell - #3313

Open
bzantium wants to merge 2 commits into
NVIDIA:mainfrom
bzantium:log-thd-dropout-composite-engine
Open

Prefer Flash Attn 2 instead of Fused Attn for THD dropout on Blackwell#3313
bzantium wants to merge 2 commits into
NVIDIA:mainfrom
bzantium:log-thd-dropout-composite-engine

Conversation

@bzantium

@bzantium bzantium commented Aug 4, 2026

Copy link
Copy Markdown

What does this PR do?

Adds a logger.debug line for the case where qkv_format="thd" and attention_dropout > 0, which is served by the composite cuDNN engine rather than the unified one.

Related to #3312.

Why

fused_attn_f16_arbitrary_seqlen.cu already notes that dropout and stats generation cannot be combined on the unified engine, so a thd request with dropout is routed to the composite engine, which does not support cu_seqlens:

      // This extra restriction is needed because cuDNN frontend doesn't yet allow
      // the combination of dropout and stats generation for the fprop unified engine,
      // so any such request would always get routed to the old composite SDPA engine
      // (which doesn't support cu_seqlens). Remove this restriction when possible.
      !is_dropout;

The routing is correct, but it is expensive and silent. Forward + backward through one DotProductAttention, 4096 tokens, 16 heads, head_dim 128, bf16, median of 50 iterations after 20 warmup:

GPU layout p=0.0 p=0.1 dropout cost
B300 (sm103, TE 2.14.1) thd 0.585 ms 3.060 ms +2.476 ms (5.24x)
B300 (sm103, TE 2.14.1) sbhd 0.480 ms 0.525 ms +0.045 ms (1.09x)
H200 (sm90, TE 2.15.0) sbhd 0.594 ms 0.614 ms +0.020 ms (1.03x)

A profile attributes it to cudnn::fusion::gen_dropout_mask_4bit and its transpose variant, which together take 43% of CUDA time in an 8-layer training step on B300.

Backend selection already logs every case where a backend is disabled, so a user reading NVTE_DEBUG_LEVEL=2 output sees why a backend was not chosen. This case is different: the backend is chosen and quietly costs several times more. Most configurations set attention_dropout to 0 and never see it, but Megatron-Core's TransformerConfig defaults it to 0.1, so a packed run that does not set it explicitly inherits the slow path with nothing in the log to suggest it.

This does not change behaviour — it only makes the situation visible. The underlying fix belongs to the cuDNN frontend restriction quoted above.

Testing

black (repo settings) and pylint --rcfile=pylintrc clean on the changed file. No behavioural change, so no new tests; the line appears in existing NVTE_DEBUG_LEVEL=2 output when the condition holds.

@github-actions github-actions Bot added the community-contribution PRs from external contributor outside the core maintainers, representing community-driven work. label Aug 4, 2026
@bzantium
bzantium marked this pull request as ready for review August 4, 2026 12:03
@bzantium
bzantium requested a review from cyanguwa as a code owner August 4, 2026 12:03
@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR changes Blackwell backend selection so THD training with dropout prefers FlashAttention 2 over the slower FusedAttention path.

  • Adds a final-selection override for SM100/SM103 when FA2 and FusedAttention are both available.
  • Logs the FusedAttention preference override after backend availability has been finalized.
  • Adds a Blackwell-specific test covering the resulting backend choice.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains; the previously reported advisory-ordering issue is resolved by placing the preference and its debug message after backend availability is finalized.

Important Files Changed

Filename Overview
transformer_engine/pytorch/attention/dot_product_attention/utils.py Adds the THD-dropout FA2 preference after all backend filters, resolving the previously reported premature and potentially misleading advisory.
tests/pytorch/attention/test_attention.py Adds architecture- and dependency-gated coverage confirming that eligible THD dropout configurations select FA2 instead of FusedAttention.

Reviews (3): Last reviewed commit: "Keep the THD dropout condition under the..." | Re-trigger Greptile

Comment thread transformer_engine/pytorch/attention/dot_product_attention/utils.py Outdated
@cyanguwa
cyanguwa requested a review from KshitijLakhani August 4, 2026 16:02
@cyanguwa

cyanguwa commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Thanks for the detailed reporting in #3312!

I agree that this affects users' performance and without warning, but I don't think it's TE's responsibility to cache these default-value issues coming from Megatron. Given that dropout is going out of fashion, Megatron can probably change their default to 0 but in a gradual deprecation code cycle. This way, it wouldn't incorrectly and silently call FlashAttention for dropout either.

With cuDNN, we can check with them to see if they can speed up the dropout implementation on Blackwell for THD, to match FlashAttention's performance. For now, we can disable FusedAttention for THD + dropout + Blackwell, and allow users to use FlashAttention instead. @KshitijLakhani, could you please lead the discussion with cuDNN, file a bug if necessary, and guide @bzantium to disable FusedAttention in this PR? Thanks!

@KshitijLakhani

Copy link
Copy Markdown
Collaborator

Thanks for the detailed reporting in #3312!

I agree that this affects users' performance and without warning, but I don't think it's TE's responsibility to cache these default-value issues coming from Megatron. Given that dropout is going out of fashion, Megatron can probably change their default to 0 but in a gradual deprecation code cycle. This way, it wouldn't incorrectly and silently call FlashAttention for dropout either.

With cuDNN, we can check with them to see if they can speed up the dropout implementation on Blackwell for THD, to match FlashAttention's performance. For now, we can disable FusedAttention for THD + dropout + Blackwell, and allow users to use FlashAttention instead. @KshitijLakhani, could you please lead the discussion with cuDNN, file a bug if necessary, and guide @bzantium to disable FusedAttention in this PR? Thanks!

@bzantium thanks for reporting this.
I definitely am able to reproduce your observations with TE 2.17 as well, so let me take up this discussion with the cuDNN team and report back once I have a good solution.
In the mean time, let me recommend a simple, no code changes WAR.

Please set NVTE_FUSED_ATTN=0 NVTE_FLASH_ATTN=1 (hopefully this unblocks you temporarily)
I independently validated this on GB200/SM100 with TE 2.17, cuDNN 9.24, and FA2 2.7.4:

  • THD FusedAttention, dropout 0.0: 0.626 ms
  • THD FusedAttention, dropout 0.1: 4.407 ms — 7.04× slower
  • THD FA2, dropout 0.0: 0.695 ms
  • THD FA2, dropout 0.1: 0.728 ms — only 1.05× slower

For THD with dropout 0.1, switching to FA2 reduced time from 4.407 ms to approximately 0.728 ms, about a 6.1× speedup.
This agrees with your B300 results:

  • FusedAttention: 0.585 → 3.060 ms, or 5.24×
  • FA2 dropout overhead: only approximately +0.024 ms

To verify that FA2 is selected, temporarily use:
NVTE_FUSED_ATTN=0 NVTE_FLASH_ATTN=1 NVTE_UNFUSED_ATTN=0 NVTE_DEBUG=1 NVTE_DEBUG_LEVEL=2

The log should report something like this:
Selected backend = FlashAttention (2.7.4.post1)

NOTE: I am suggesting NVTE_UNFUSED_ATTN=0 only as a validation guard to prevent silent fallback.

@KshitijLakhani KshitijLakhani self-assigned this Aug 6, 2026
Comment thread transformer_engine/pytorch/attention/dot_product_attention/utils.py Outdated
Comment on lines 1632 to 1638

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replace the existing Hopper+ FusedAttention preference block with the following narrow selection rule. This safely prefers FA2 only when it is confirmed installed, enabled, and eligible. Otherwise, FusedAttention remains enabled.

      # Prefer FA2 for THD training with dropout on SM100/103, where FusedAttention has a known
      # performance issue. At this point use_flash_attention_2 confirms a usable installation.
      if (
          is_training
          and qkv_format == "thd"
          and attention_dropout != 0.0
          and device_compute_capability in ((10, 0), (10, 3))
          and use_flash_attention_2
          and use_fused_attention
      ):
          logger.debug(
              "Disabling FusedAttention to give FlashAttention 2 preference for THD with dropout on SM100/103"
          )
          use_fused_attention = False
          fused_attention_backend = None
      # Select FusedAttention for performance in all other Hopper+ configurations.
      elif use_flash_attention and use_fused_attention and device_compute_capability >= (9, 0):
          logger.debug(
              "Disabling FlashAttention to give FusedAttention preference on Hopper+ "
              "for performance reasons"
          )
          use_flash_attention = False

Please place this after unavailable FlashAttention installations have been filtered and use_flash_attention_2 has been finalized. This placement is important because it ensures that FusedAttention is disabled only when FA2 is actually usable.

@KshitijLakhani

KshitijLakhani commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

@bzantium Here's a quick update: I discussed with the cuDNN team and it's a known shortcoming. The issue is not unified v/s composite but it is the slow dropout kernels used for this very specific combination being discussed here. The fact that the slow dropout kernels are being triggered in a composite setup worsens the timing. cuDNN does not have this on their roadmap for the foreseeable future as it is low priority and the suggested "fix" is for TE users to use FA2 instead if performance is of importance.

If you'd like to contribute to TE, which we highly encourage, I've reviewed your PR and added suggested changes. Please review these changes and commit them. Once you've done this I can help you launch CI on this PR and review/approve the PR to have it merged in to main.

On the contrary, if you'd rather have me add in these changes, I'm happy to do so in a separate PR. Let me know.

Notes:

  • Please make your branch up-to-date with main
  • Please change the title of the PR to something more suitable now that we know it isn't a composite engine problem per se. A suggestion is Prefer Flash Attn 2 instead of Fused Attn for THD dropout on Blackwell but feel free to use your own

@KshitijLakhani

Copy link
Copy Markdown
Collaborator

Lastly, here's a small mock up test I came up with using my agent. I just asked it to write a test to confirm the backend choice. This is juts a guidance but it does the trick. Feel free to be creative :)

  @pytest.mark.skipif(
      device_compute_capability not in ((10, 0), (10, 3)),
      reason="This backend preference applies only to SM100/SM103.",
  )
  @pytest.mark.skipif(
      not FlashAttentionUtils.is_installed,
      reason="A supported FlashAttention 2 installation is required.",
  )
  @pytest.mark.skipif(
      get_cudnn_version() < (9, 0, 0),
      reason="cuDNN 9.0.0+ is required for THD FusedAttention.",
  )
  def test_thd_dropout_prefers_flash_attention_2(monkeypatch):
      # Ensure both candidate backends are enabled for this test. Pytest restores
      # the previous environment automatically after the test completes.
      monkeypatch.setenv("NVTE_FLASH_ATTN", "1")
      monkeypatch.setenv("NVTE_FLASH_ATTN_V2", "1")
      monkeypatch.setenv("NVTE_FUSED_ATTN", "1")
      attention_params = AttentionParams(
          qkv_dtype=torch.bfloat16,
          qkv_layout="thd_thd_thd",
          batch_size=4,
          num_heads=16,
          num_gqa_groups=16,
          max_seqlen_q=1024,
          max_seqlen_kv=1024,
          head_dim_qk=128,
          head_dim_v=128,
          attn_mask_type="padding_causal",
          core_attention_bias_shape=None,
          attention_dropout=0.1,
          is_training=True,
      )
      (
          use_flash_attention,
          flash_attention_backend,
          use_fused_attention,
          fused_attention_backend,
          _,
          available_backends,
      ) = get_attention_backend(attention_params)
      assert available_backends[0]  # Real FA2 support check
      assert available_backends[1]  # Real cuDNN FusedAttention support check
      assert use_flash_attention
      assert flash_attention_backend == FlashAttentionUtils.version
      assert not use_fused_attention
      assert fused_attention_backend is None

@KshitijLakhani

Copy link
Copy Markdown
Collaborator

@bzantium just circling back to check in if you were able to try the suggested changes ?
Please let me know if you'd prefer me to create a separate PR to fix the issue. Thanks !

cuDNN's dropout kernels for THD attention are much slower than FlashAttention 2's
on SM100/103, and the cuDNN team confirmed a fix is not on their roadmap. The
generic Hopper+ rule prefers FusedAttention, so this combination silently took the
slow path with nothing reporting it.

Prefer FA2 for THD training with dropout on SM100/103 when FA2 is confirmed
usable, and leave every other configuration on the existing rule. The earlier
advisory is dropped: it ran before backend selection was final and could name
FusedAttention when a later filter selected something else.

Signed-off-by: Minho Ryu <ryumin93@gmail.com>
@bzantium bzantium changed the title Log when thd with dropout falls to the composite cuDNN engine Prefer Flash Attn 2 instead of Fused Attn for THD dropout on Blackwell Aug 13, 2026
@bzantium
bzantium force-pushed the log-thd-dropout-composite-engine branch from e621034 to 54f463b Compare August 13, 2026 00:50
@bzantium

bzantium commented Aug 13, 2026

Copy link
Copy Markdown
Author

@KshitijLakhani thanks for chasing this with the cuDNN team, and sorry for the slow reply.

Took both suggestions in 54f463b. The advisory is gone, and the FA2 preference now sits after use_flash_attention_2 is finalized so FusedAttention only gets disabled when FA2 is really usable. Rebased on main and retitled too.

For the test I stayed close to your mock-up but added assert available_backends[0] and [1] up front, otherwise a box without FA2 or cuDNN would pass for the wrong reason. Kept it in test_attention.py since the QA scripts name test files one by one.

Ran it on an SM103 box with both backends available: FusedAttention (NVTE_F16_arbitrary_seqlen) before, FA2 after. Happy for you to kick off CI whenever.

@bvolpato bvolpato left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other than these two questions, this looks good to me. Preference is narrowly scoped and runs after backend eligibility is finalized, and the test covers the intended selection.

# Prefer FA2 for THD training with dropout on SM100/103, where FusedAttention has a known
# performance issue. At this point use_flash_attention_2 confirms a usable installation.
if (
is_training

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we simplify this condition or add a targeted too-many-boolean-expressions disable? The repo’s pinned Pylint reports R0916 here (6/5), so qa/L0_pytorch_lint/test.sh should fail once full CI runs. Is that a concern?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, real concern. Reproduced it with the repo's pylintrc and pinned pylint 3.3.1: R0916: Too many boolean expressions in if statement (6/5).

Split the condition rather than disabling the check, since the hardware and workload half wants a name anyway:

is_slow_fused_thd_dropout = (
    is_training
    and qkv_format == "thd"
    and attention_dropout != 0.0
    and device_compute_capability in ((10, 0), (10, 3))
)
if is_slow_fused_thd_dropout and use_flash_attention_2 and use_fused_attention:

File is back to 10.00/10 in a310413.

the roadmap, so the choice is made here rather than left to the generic Hopper+ rule
that prefers FusedAttention.
"""
monkeypatch.setenv("NVTE_FLASH_ATTN", "1")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also set NVTE_FLASH_ATTN_V2=1 here? get_attention_backend reads that flag independently, so a run inheriting NVTE_FLASH_ATTN_V2=0 reaches this test and fails available_backends[0] even though FA2 is intentionally disabled. Is that a concern for test isolation?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, NVTE_FLASH_ATTN_V2 is read separately at utils.py:525, so inheriting NVTE_FLASH_ATTN_V2=0 would fail the availability assertion rather than skip. Pinned it in a310413 with a note on why.

The six-term condition tripped R0916 (6/5), which the repo does not disable, so
qa/L0_pytorch_lint would have failed. Naming the hardware and workload half also
says what the condition is for.

Pin NVTE_FLASH_ATTN_V2 in the test as well. get_attention_backend reads it apart
from NVTE_FLASH_ATTN, so an environment that disables FA2 on purpose would fail
the availability assertion instead of skipping.

Signed-off-by: Minho Ryu <ryumin93@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution PRs from external contributor outside the core maintainers, representing community-driven work.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants